home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 3 / Info_Mac_1994-01.iso / Development / Information / Mac Programming Secrets 1.0.1 / Chapter 05 / Neat Stuff.c < prev    next >
C/C++ Source or Header  |  1992-05-19  |  18KB  |  588 lines

  1. #include "Neat Stuff.h"
  2. #include "StdLib.h"                        // for abs()
  3. #include "GestaltEqu.h"
  4. #include "QDOffscreen.h"
  5.  
  6. #define NIL 0L
  7. const short kWindowID = 128;
  8. const short kDialogID = 130;
  9.  
  10. GDHandle    pOldDevice;
  11. CGrafPtr    pOldPort;
  12.  
  13. /*******************************************************************************
  14.  
  15.     DoNewWindow
  16.  
  17.     Simply creates a new window and attaches a randomly created QuickDraw
  18.     picture to it to handle the updates.
  19.  
  20. *******************************************************************************/
  21. void    DoNewWindow(void)
  22. {
  23.     WindowPtr    roomWithAView;
  24.     PicHandle    picturePerfect;
  25.  
  26.     roomWithAView = GetNewWindow(kWindowID, NIL, (WindowPtr) -1);
  27.     if (roomWithAView != NIL) {
  28.         SetPort(roomWithAView);
  29.         picturePerfect = GetRandomPicture(&(roomWithAView->portRect));
  30.         SetWindowPic(roomWithAView, picturePerfect);
  31.         ShowWindow(roomWithAView);
  32.     }
  33. }
  34.  
  35.  
  36. /*******************************************************************************
  37.  
  38.     GetRandomPicture
  39.  
  40.     Creates a random picture that fits within the bounds given us. First, we
  41.     generate a random number between 40 and 60; this is the number of shapes
  42.     we’ll stick in the picture. Next, for each shape, we generate 2 random
  43.     coordinates within our limiting bounds. After that, we choose a random
  44.     shape to create, and use our two points to create it. When we’ve created
  45.     all of our shapes, we close out the picture and return it to the caller.
  46.  
  47. *******************************************************************************/
  48. PicHandle GetRandomPicture(Rect* bounds)
  49. {
  50.     short        width;
  51.     short        height;
  52.     PicHandle    pitcher;
  53.     int            numberOfShapes;
  54.     int            loopy;
  55.     Point        p1;
  56.     Point        p2;
  57.     Point        penSize;
  58.     Rect        randomRect;
  59.     int            shapeKind;
  60.  
  61.     qd.randSeed = TickCount();
  62.     width = bounds->right - bounds->left;
  63.     height = bounds->bottom - bounds->top;
  64.     pitcher = OpenPicture(bounds);
  65.     PenMode(patXor);
  66.     numberOfShapes = abs(Random() % 20) + 40;            // 40 to 60 shapes
  67.     for (loopy = 0; loopy < numberOfShapes; loopy++) {
  68.         p1.h = abs(Random() % width) + bounds->left;
  69.         p1.v = abs(Random() % height) + bounds->top;
  70.         p2.h = abs(Random() % width) + bounds->left;
  71.         p2.v = abs(Random() % height) + bounds->top;
  72.         penSize.h = abs(Random() % 10) + 1;
  73.         penSize.v = abs(Random() % 10) + 1;
  74.         PenSize(penSize.h, penSize.v);
  75.         Pt2Rect(p1, p2, &randomRect);
  76.         shapeKind = abs(Random() % 4);
  77.         switch (shapeKind) {
  78.             case 0:
  79.                 MoveTo(p1.h, p1.v);
  80.                 LineTo(p2.h, p2.v);
  81.                 break;
  82.             case 1:
  83.                 FrameRect(&randomRect);
  84.                 break;
  85.             case 2:
  86.                 FrameOval(&randomRect);
  87.                 break;
  88.             case 3:
  89.                 FrameRoundRect(&randomRect, 16, 16);
  90.                 break;
  91.         }
  92.     }
  93.     ClosePicture();
  94.     return pitcher;
  95. }
  96.  
  97.  
  98. /*******************************************************************************
  99.  
  100.     ShowDialog
  101.  
  102.     Shows a simple modal dialog. Nothing fancy; it’s used as a to contrast to
  103.     ShowSavingDialog, which saves the background first and later restores it.
  104.  
  105. *******************************************************************************/
  106. void    ShowDialog(void)
  107. {
  108.     DialogPtr        dlg;
  109.     short            item;
  110.  
  111.     dlg = GetNewDialog(kDialogID, NIL, (WindowPtr) -1);
  112.  
  113.     SetPort(dlg);
  114.     ShowWindow(dlg);
  115.     do {
  116.         ModalDialog(NIL, &item);
  117.     } while ((item != ok) && (item != cancel));
  118.     DisposeDialog(dlg);
  119. }
  120.  
  121.  
  122. /*******************************************************************************
  123.  
  124.     ShowSavingDialog
  125.  
  126.     This is the same as ShowDialog, except that it saves the background before
  127.     showing the window, and restores the background after the dialog has been
  128.     put away.
  129.  
  130. *******************************************************************************/
  131. void    ShowSavingDialog(void)
  132. {
  133.     DialogPtr        dlg;
  134.     short            item;
  135.     SavedBGHandle    savedData;
  136.  
  137.     dlg = GetNewDialog(kDialogID, NIL, (WindowPtr) -1);
  138.  
  139.     SaveBackground(dlg, &savedData);
  140.  
  141.     SetPort(dlg);
  142.     ShowWindow(dlg);
  143.     do {
  144.         ModalDialog(NIL, &item);
  145.     } while ((item != ok) && (item != cancel));
  146.     DisposeDialog(dlg);
  147.  
  148.     RestoreBackground(savedData);
  149. }
  150.  
  151.  
  152. /*******************************************************************************
  153.  
  154.     SaveBackground
  155.  
  156.     Given an invisible window that we’d like to show, this function determines
  157.     what parts of our background windows it will cover, and saves those bits
  158.     in an offscreen buffer. Information pertaining to the area of the saved
  159.     background, as well as the saved bits themselves, is returned in the
  160.     SavedBGHandle and passed back to the caller. This handle is later passed
  161.     to RestoreBackground to refresh the screen.
  162.  
  163. *******************************************************************************/
  164. void SaveBackground(WindowPtr w, SavedBGHandle* savedData)
  165. {
  166.     RgnHandle    coveredWagon;
  167.     Rect        coveredRect;
  168.     GrafPtr        offWorld;
  169.     GrafPtr        desktopPort;
  170.  
  171.     coveredWagon = GetCoveredArea(w);
  172.     if (!EmptyRgn(coveredWagon)) {
  173.         coveredRect = (**coveredWagon).rgnBBox;
  174.         offWorld = CreateOffWorld(coveredRect);
  175.     
  176.         UseOffWorld(offWorld);
  177.         desktopPort = GetDesktopPort();
  178.         CopyBits(&desktopPort->portBits, &offWorld->portBits, &coveredRect, &offWorld->portRect, srcCopy, NIL);
  179.         DoneWithOffWorld(offWorld);
  180.     
  181.         *savedData = (SavedBGHandle) NewHandle(sizeof(SavedBGRecord));
  182.     
  183.         (***savedData).coveredArea = coveredWagon;
  184.         (***savedData).offWorld = offWorld;
  185.     } else {
  186.         *savedData = NIL;
  187.     }
  188. }
  189.  
  190.  
  191. /*******************************************************************************
  192.  
  193.     RestoreBackground
  194.  
  195.     Given the data saved in SaveBackground, restore the screen, being careful
  196.     to alter only the contents of _our_ windows and no one else’s.
  197.  
  198. *******************************************************************************/
  199. void RestoreBackground(SavedBGHandle savedData)
  200. {
  201.     RgnHandle    coveredWagon;
  202.     Rect        coveredRect;
  203.     GrafPtr        offWorld;
  204.     GrafPtr        desktopPort = GetDesktopPort();
  205.  
  206.     if (savedData != NIL) {
  207.         coveredRect = (**(**savedData).coveredArea).rgnBBox;
  208.         UseOffWorld((**savedData).offWorld);
  209.         SetPort(desktopPort);
  210.         ClipRect(&coveredRect);
  211.         SetDesktopDevice();
  212.         CopyBits(&(**savedData).offWorld->portBits, &desktopPort->portBits,
  213.                 &(**savedData).offWorld->portRect, &coveredRect,
  214.                 srcCopy, (**savedData).coveredArea);
  215.         DoneWithOffWorld((**savedData).offWorld);
  216.     
  217.         ValidateWindows((**savedData).coveredArea);
  218.         DisposeOffWorld((**savedData).offWorld);
  219.         DisposeRgn((**savedData).coveredArea);
  220.         DisposeHandle((Handle) savedData);
  221.     }
  222. }
  223.  
  224.  
  225. /*******************************************************************************
  226.  
  227.     GetDesktopPort
  228.  
  229.     If we are running under a Color QuickDraw environment, return the color
  230.     Window Manager port. Otherwise, return the B&W Window Manager port.
  231.  
  232. *******************************************************************************/
  233. GrafPtr GetDesktopPort()
  234. {
  235.     OSErr    err;
  236.     long    value;
  237.     GrafPtr    desktopPort;
  238.  
  239.     err = Gestalt(gestaltQuickdrawVersion, &value);
  240.  
  241.     if ((err == noErr) && (value >= gestalt8BitQD))
  242.         GetCWMgrPort((CGrafPtr*) &desktopPort);
  243.     else
  244.         GetWMgrPort(&desktopPort);
  245.  
  246.     return desktopPort;
  247. }
  248.  
  249.  
  250. /*******************************************************************************
  251.  
  252.     SetDesktopDevice
  253.  
  254.     If we are running on a Mac with Color QuickDraw, set the current GDevice
  255.     to be the main device. Otherwise, do nothing, as GDevices aren’t
  256.     implemented on this machine.
  257.  
  258. *******************************************************************************/
  259. void SetDesktopDevice()
  260. {
  261.     OSErr    err;
  262.     long    value;
  263.  
  264.     err = Gestalt(gestaltQuickdrawVersion, &value);
  265.  
  266.     if ((err == noErr) && (value >= gestalt8BitQD))
  267.         SetGDevice(GetMainDevice());
  268. }
  269.  
  270.  
  271. /*******************************************************************************
  272.  
  273.     GetCoveredArea
  274.  
  275.     Given an invisible window, determine the areas of all our other windows
  276.     that would be obscured by it. Return this area as a QuickDraw region in
  277.     global coordinates.
  278.  
  279.     This routine starts by getting the union of the visRgns of all our
  280.     windows. As we traverse the windows, we get their visRgns, translate them
  281.     into global coordinates, and merge them into one big region. After that,
  282.     we find the rectangle the bounds our invisible window, convert it into a
  283.     region, too, and intersect it with our big union of visRgns. This
  284.     resulting region is returned to the caller.
  285.  
  286. *******************************************************************************/
  287. RgnHandle GetCoveredArea(WindowPtr w)
  288. {
  289.     Rect        wRect;
  290.     RgnHandle    wRgn;
  291.     RgnHandle    coveredRgn;
  292.     WindowPtr    thisWindow;
  293.     Point        windowLocalTopLeft;
  294.     Point        delta;
  295.  
  296.     coveredRgn = NewRgn();
  297.     thisWindow = FrontWindow();
  298.     while (thisWindow != NIL) {
  299.         windowLocalTopLeft = topLeft(thisWindow->portRect);
  300.         delta = GetGlobalTopLeft(thisWindow);
  301.         SubPt(windowLocalTopLeft, &delta);
  302.         OffsetRgn(thisWindow->visRgn, delta.h, delta.v);
  303.         UnionRgn(thisWindow->visRgn, coveredRgn, coveredRgn);
  304.         OffsetRgn(thisWindow->visRgn, -delta.h, -delta.v);
  305.         thisWindow = (WindowPtr) ( (WindowPeek) thisWindow)->nextWindow;
  306.     }
  307.  
  308.     wRgn = NewRgn();
  309.     wRect = GetWindowStructureRect(w);
  310.     RectRgn(wRgn, &wRect);
  311.     SectRgn(wRgn, coveredRgn, coveredRgn);
  312.     DisposeRgn(wRgn);
  313.  
  314.     return coveredRgn;
  315. }
  316.  
  317.  
  318. /*******************************************************************************
  319.  
  320.     CreateOffWorld
  321.  
  322.     Given a rectangle, create an offscreen buffer with the same bounds. If we
  323.     are running on a Mac with the GWorld routines, use them. Otherwise, as
  324.     long as we are running on a Classic QuickDraw machine, whip one up by
  325.     hand. In this little sample, we don’t support the creation of color
  326.     offscreen buffers on Color QuickDraw machines that don’t have the GWorld
  327.     routines.
  328.  
  329. *******************************************************************************/
  330. GrafPtr CreateOffWorld(Rect globalRect)
  331. {
  332.     OSErr        err;
  333.     long        value;
  334.     long        height;
  335.     long        width;
  336.     short        depth;
  337.     BitMap        bitMap;
  338.     GrafPtr        oldPort;
  339.     GDHandle    oldDevice;
  340.     GrafPtr        newPortBeach;
  341.     GWorldPtr    braveNewGWorld;
  342.  
  343.     err = Gestalt(gestaltQuickdrawVersion, &value);
  344.  
  345.     if (err == noErr) {
  346.         if (value >= gestalt32BitQD12) {
  347.  
  348.             err = NewGWorld(&braveNewGWorld,    // result
  349.                             0,                    // pixel size (0 = find deepest)
  350.                             &globalRect,        // bounds
  351.                             NIL,                // color table (NIL = default)
  352.                             NIL,                // gDevice (not used in this call)
  353.                             0);                    // flags
  354.  
  355.             GetGWorld((CGrafPtr*) &oldPort, &oldDevice);
  356.             UseOffWorld((GrafPtr) braveNewGWorld);
  357.             EraseRect(&braveNewGWorld->portRect);
  358.             DoneWithOffWorld((GrafPtr) braveNewGWorld);
  359.             SetGWorld((CGrafPtr) oldPort, oldDevice);
  360.  
  361.             return (GrafPtr) braveNewGWorld;
  362.  
  363.         } else if (value == gestaltOriginalQD) {
  364.  
  365.             height = globalRect.bottom - globalRect.top;
  366.             width = globalRect.right - globalRect.left;
  367.  
  368.             bitMap.rowBytes = (((width - 1) / 32) + 1) * 4;
  369.             bitMap.bounds = globalRect;
  370.             bitMap.baseAddr = (Ptr) NewHandle(height * bitMap.rowBytes);
  371.  
  372.             newPortBeach = (GrafPtr) NewPtr(sizeof(GrafPort));
  373.             OpenPort(newPortBeach);
  374.             SetPortBits(&bitMap);
  375.             newPortBeach->portRect = globalRect;
  376.             ClipRect(&globalRect);
  377.             CopyRgn(newPortBeach->clipRgn, newPortBeach->visRgn);
  378.  
  379.             GetPort(&oldPort);
  380.             SetPort(newPortBeach);
  381.             EraseRect(&braveNewGWorld->portRect);
  382.             SetPort(oldPort);
  383.  
  384.             return newPortBeach;
  385.  
  386.         } else {
  387.  
  388.             DebugStr("\pSorry, unsupported QuickDraw version.");
  389.  
  390.         }
  391.     }
  392.     return NIL;
  393. }
  394.  
  395.  
  396. /*******************************************************************************
  397.  
  398.     UseOffWorld
  399.  
  400.     This is sort of like a SetPort routine for our offscreen buffers. If we
  401.     have created a color offscreen buffer with the GWorld routines, lock down
  402.     our pixels and call SetGWorld to set the grafPort and the GDevice. If we
  403.     are running on a B&W machine, lock down our bits and simply call SetPort.
  404.     In both cases, remember the current GrafPort and (if appropriate) GDevice
  405.     so that we can restore them later in DoneWithGWorld.
  406.  
  407. *******************************************************************************/
  408. void UseOffWorld(GrafPtr offWorlder)
  409. {
  410.     if (IsColorPort(offWorlder)) {
  411.         GetGWorld(&pOldPort, &pOldDevice);
  412.         (void) LockPixels(GetGWorldPixMap((GWorldPtr) offWorlder));
  413.         SetGWorld((CGrafPtr) offWorlder, NIL);
  414.     } else {
  415.         GetPort((GrafPtr*) &pOldPort);
  416.         HLock((Handle) offWorlder->portBits.baseAddr);
  417.         offWorlder->portBits.baseAddr = *(Handle) (offWorlder->portBits.baseAddr);
  418.         SetPort(offWorlder);
  419.     }
  420. }
  421.  
  422.  
  423. /*******************************************************************************
  424.  
  425.     DoneWithOffWorld
  426.  
  427.     This undoes the effects of UseOffWorld. If we are running with the GWorld
  428.     routines, unlock our pixels and reset the GrafPort and GDevice to what
  429.     they were before we called UseOffWorld. If we are running on a B&W
  430.     machine, unlock our bits and restore the old GrafPort.
  431.  
  432. *******************************************************************************/
  433. void DoneWithOffWorld(GrafPtr offWorlder)
  434. {
  435.     if (IsColorPort(offWorlder)) {
  436.         UnlockPixels(GetGWorldPixMap((GWorldPtr) offWorlder));
  437.         SetGWorld(pOldPort, pOldDevice);
  438.     } else {
  439.         offWorlder->portBits.baseAddr = (Ptr) RecoverHandle(offWorlder->portBits.baseAddr);
  440.         HUnlock((Handle) offWorlder->portBits.baseAddr);
  441.         SetPort((GrafPtr) pOldPort);
  442.     }
  443. }
  444.  
  445.  
  446. /*******************************************************************************
  447.  
  448.     IsColorPort
  449.  
  450.     Simple utility for determining if the offscreen buffer represents color or
  451.     B&W pixels. Since we only create a color buffer if the GWorld routines are
  452.     available to us, we often use this utility to see if we can call the
  453.     GWorld functions in subsequent routines.
  454.  
  455. *******************************************************************************/
  456. Boolean IsColorPort(GrafPtr offWorlder)
  457. {
  458.     return (offWorlder->portBits.rowBytes & 0x08000) != 0;
  459. }
  460.  
  461.  
  462. /*******************************************************************************
  463.  
  464.     DisposeOffWorld
  465.  
  466.     Release the memory for our offscreen buffer. If we created the buffer with
  467.     NewGWorld, call DisposeGWorld. If we created the buffer by hand, we
  468.     dispose of the two blocks of data we created -- the buffer for the
  469.     GrafPort, and the buffer for the bits themselves.
  470.  
  471. *******************************************************************************/
  472. void DisposeOffWorld(GrafPtr offWorlder)
  473. {
  474.     if (IsColorPort(offWorlder)) {
  475.         DisposeGWorld((GWorldPtr) offWorlder);
  476.     } else {
  477.         DisposeHandle((Handle) offWorlder->portBits.baseAddr);
  478.         ClosePort(offWorlder);
  479.         DisposePtr((Ptr) offWorlder);
  480.     }
  481. }
  482.  
  483.  
  484. /*******************************************************************************
  485.  
  486.     ValidateWindows
  487.  
  488.     Given a region in global coordinates, validate the parts of all the
  489.     windows it intersects. To do this, we traverse all open windows, starting
  490.     with FrontWindow(). For each window, we make a temporary copy of
  491.     globalRgn, translate that copy into the window’s local coordinate system,
  492.     and then call ValidRgn with that copy.
  493.  
  494. *******************************************************************************/
  495. void ValidateWindows(RgnHandle globalRgn)
  496. {
  497.     GrafPtr        oldPort;
  498.     RgnHandle    tempRgn;
  499.     WindowPtr    thisWindow;
  500.     Point        windowLocalTopLeft;
  501.     Point        delta;
  502.  
  503.     GetPort(&oldPort);
  504.     tempRgn = NewRgn();
  505.     thisWindow = FrontWindow();
  506.     while (thisWindow != NIL) {
  507.         SetPort(thisWindow);
  508.         windowLocalTopLeft = topLeft(thisWindow->portRect);
  509.         delta = windowLocalTopLeft;
  510.         LocalToGlobal(&delta);
  511.         SubPt(windowLocalTopLeft, &delta);
  512.         CopyRgn(globalRgn, tempRgn);
  513.         OffsetRgn(tempRgn, -delta.h, -delta.v);
  514.         SectRgn(thisWindow->visRgn, tempRgn, tempRgn);
  515.         ValidRgn(tempRgn);
  516.         thisWindow = (WindowPtr) ( (WindowPeek) thisWindow)->nextWindow;
  517.     }
  518.     DisposeRgn(tempRgn);
  519.     SetPort(oldPort);
  520. }
  521.  
  522.  
  523. /*******************************************************************************
  524.  
  525.     GetWindowStructureRect
  526.  
  527.     This procedure is used to get the rectangle that surrounds the entire
  528.     structure of a window. This works whether or not the window is visible. If
  529.     the window is visible, it is a simple matter of using the bounding
  530.     rectangle of the structure region. If the window is invisible, the
  531.     strucRgn is not valid. To make it valid, the window has to be moved way
  532.     off the screen and then made visible. This generates a valid strucRgn,
  533.     although it is valid for the position that is way off the screen. It still
  534.     needs to be offset back into the original position. Once the bounding
  535.     rectangle for the strucRgn is obtained, the window can then be hidden
  536.     again and moved back to its correct location. Note that ShowHide is used,
  537.     instead of ShowWindow and HideWindow. HideWindow can change the plane of
  538.     the window. Also, ShowHide does not affect the hiliting of windows.
  539.  
  540. *******************************************************************************/
  541. Rect    GetWindowStructureRect(WindowPtr window)
  542. {
  543.     const short    kOffscreenLocation = 0x4000;    /* Halfway to infinity */
  544.  
  545.     GrafPtr        oldPort;
  546.     Rect        structureRect;
  547.     Point        windowLoc;
  548.  
  549.     if (((WindowPeek)window)->visible)
  550.         structureRect = (*(((WindowPeek)window)->strucRgn))->rgnBBox;
  551.     else {
  552.         GetPort(&oldPort);
  553.         SetPort(window);
  554.         windowLoc = GetGlobalTopLeft(window);
  555.         MoveWindow(window, windowLoc.h, kOffscreenLocation, FALSE);
  556.         ShowHide(window, TRUE);
  557.         structureRect = (*(((WindowPeek) window)->strucRgn))->rgnBBox;
  558.         ShowHide(window, FALSE);
  559.         MoveWindow(window, windowLoc.h, windowLoc.v, FALSE);
  560.         OffsetRect(&structureRect, 0, windowLoc.v - kOffscreenLocation);
  561.         SetPort(oldPort);
  562.     }
  563.     return structureRect;
  564. }
  565.  
  566.  
  567. /*******************************************************************************
  568.  
  569.     GetGlobalTopLeft
  570.  
  571.     Return the top left point of the given window’s port in global
  572.     coordinates. This returns the top left point of the window’s content area
  573.     only; it doesn’t include the window’s drag region (or title bar).
  574.  
  575. *******************************************************************************/
  576. Point    GetGlobalTopLeft(WindowPtr window)
  577. {
  578.     GrafPtr            oldPort;
  579.     Point            globalPt;
  580.  
  581.     GetPort(&oldPort);
  582.     SetPort(window);
  583.     globalPt = topLeft(window->portRect);
  584.     LocalToGlobal(&globalPt);
  585.     SetPort(oldPort);
  586.     return globalPt;
  587. }
  588.